#pragma once

#include "deviceManager.hpp"
#include "msgObj.hpp"

namespace Protocol {

class DevAddMsgObject : public MsgObject {
public:
    DevAddMsgObject(std::shared_ptr<MqttClient> m, std::shared_ptr<DeviceManager> d) : MsgObject(m, d){};

public:
    std::size_t tick;
    std::size_t time;
    std::string src;

    struct Ack {
        std::size_t tick;
        std::size_t time;
        friend void to_json(nlohmann::json &j, const Ack &a) { j = {{"tick", a.tick}, {"time", a.time}}; }
        Ack &setTickAndTime(std::size_t tick, std::size_t time) {
            this->tick = tick;
            this->time = time;
            return *this;
        }
    } ack;

    struct Dev : public Device {
        std::string status;

        Dev &setStatus(bool stat) {
            status = stat ? "success" : "failed";
            return *this;
        }

        friend void to_json(nlohmann::json &j, const Dev &d) {
            to_json(j, static_cast<const Device &>(d));
            j["status"] = d.status;
        }
    };

    std::vector<Dev> tmpDev;

    friend void from_json(const nlohmann::json &j, DevAddMsgObject &d) {
        if (j.contains("tick")) {
            j.at("tick").get_to(d.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(d.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(d.src);
        }

        if (j.contains("tmp_dev") && j.at("tmp_dev").is_array()) {
            for (const auto &dev : j.at("tmp_dev")) {
                d.tmpDev.emplace_back(dev);
            }
        }
    }

    friend void to_json(nlohmann::json &j, const DevAddMsgObject &d) {
        j = {{"tick", d.tick}, {"time", d.time}, {"src", d.src}, {"ack", d.ack}};
        for (const auto &dev : d.tmpDev) {
            j["tmp_dev"].emplace_back(dev);
        }
    }

public:
    virtual bool parse(const std::string &topic, const std::string &payload) override {
        auto dest = subTopic(topic, 4);
        if (dest != selfSrc) {
            return false;
        }

        if (!nlohmann::json::accept(payload)) {
            return false;
        }

        nlohmann::json::parse(payload).get_to(*this);
        return true;
    }

    virtual MsgObject &action(void) override {
        for (auto &dev : tmpDev) {
            _device->addDevice(dev);
            dev.setStatus(true);
        }
        return *this;
    }

    virtual bool reply(void) override {
        ack.setTickAndTime(tick, std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
        auto topic = "v1/dev/" + selfSrc + "/" + src + "/dev/add_ack";
        src = selfSrc;
        return _mqtt->publish(topic, nlohmann::json(*this).dump());
    }
};
}; // namespace Protocol
